package main

import (
	"fmt"
	"math/rand"
	"sort"
)

func main() {
	/*
			1、看下面一段程序，回答问题
			s1 := make([]int, 3, 4)
		    s2 := append(s1, 1)
		请问s1、s2内各有什么元素？
		s1修改一个元素会影响s2吗？s2修改一个元素会影响s1吗？
		s2再增加一个元素会怎么样？
	*/
	/*
		答：s1:[0 0 0],长度为3，容量为4； s2:[0 0 0 1]，由于s2在末尾追加一个1，并没有超出原有容量，没有进行扩容，所以长度为4，容量还是4；
		s2是复制了s1的header，底层数组一样，然后在末尾追加一个元素，但s2的len<=cap，没有进行扩容，所以s1和s2当前共用一个底层数组，由于s1的长度为3，小于s2的长度，所以修改前三个元素，s1和s2都会影响，但是如果s2修改的元素是第4个，不会影响s1；
		s2再增加一个元素，因为len>cap，所以需要扩容，会创建一个新的数组，把原有元素复制进去，再追加一个新的元素，header中的指针更新，指向新的数组地址，len为5，cap翻一倍变成8，至此s2和s1不共用底层数组了，他们的更新也不会互相影响了。
	*/

	/*
		2、有一个数组 [1,4,9,16,2,5,10,15]，生成一个新切片，要求新切片元素是数组相邻2项的和。
	*/
	a1 := [8]int{1, 4, 9, 16, 2, 5, 10, 15}
	s1 := make([]int, 0, len(a1))
	for i := 1; i < len(a1); i++ {
		s1 = append(s1, a1[i-1]+a1[i])
	}
	fmt.Println("数组相邻2项的和:", s1) //[5 13 25 18 7 15 25]

	/*
		3、数字重复统计
		随机产生100个整数
		数字的范围[-100, 100]
		降序输出这些生成的数字并打印其重复的次数
	*/
	//生成随机数
	s2 := make([]int, 100)
	for i := 0; i < 100; i++ {
		s2[i] = rand.Intn(201) - 100
	}
	//倒序排列
	sort.Sort(sort.Reverse(sort.IntSlice(s2)))
	fmt.Println("降序输出[-100, 100]范围内的随机数:", s2)
	//统计重复次数
	m2 := make(map[int]int)
	for i := 0; i < len(s2); i++ {
		m2[s2[i]] += 1
	}
	for k, v := range m2 {
		fmt.Printf("数字：%d，重复%d次\n", k, v)
	}

}

/*
	4、简述线性数据结构和哈希表优劣
	答：
	线性表：
	优点：
	通过索引直接访问元素，时间复杂度为 O(1)，访问效率非常高。
	切片支持扩容，并且切片复制，只复制header，它们可以共用底层数组，节省内存空间；
	缺点：
	顺序表在开头和中间插入、删除元素时，需要移动后面所有元素，时间复杂度为O(n)，效率相对较低。
	数组长度和容量固定，不支持扩容和删除元素。

	哈希表：
	优点：
	map 类型的检索时间复杂度O(1)，因此在大型数据集的查找场景中表现出色；
	哈希表通常使用拉链法来处理hash冲突，这意味着只有在必要时才分配额外的空间，空间利用率高；

	缺点：
	map 类型的底层实现需要占用一定的内存空间，当元素数量较大时，可能会造成内存占用过大的问题。
	零值不可用，必须通过make或字面常量构造。
	由于哈希表使用的是哈希函数进行元素的散列，因此可能会导致多个键映射到同一个值的情况，导致哈希冲突。
	哈希表本质上是无序的，这使得它适合于查找和插入元素，但不适合对元素进行排序操作。
*/

// 批改意见
// 1. 思路清晰，逻辑正确，最后一题可以尝试一下输出排序元素的同时，输出它的统计次数